#include "iotgo_platformAPI.h"
#include "iotgo_memory_hooks.h"
#include "iotgo_pair_with_app.h"
#include "iotgo_mySocketServer_interface.h"
#include "iotgo_debug.h"
#include "cJSON.h"
#include "assert.h"

#define debugPrint pairWithAppDebugPrint

#ifndef FAIL
#define FAIL (-1)
#endif

#define PAIR_NOT_START              0
#define PAIR_AP_IS_READY            1
#define PAIR_SERVER_OBJ_IS_READY    2
#define PAIR_APP_IS_ACCESS          3

MySocketServer *p_server = NULL;
volatile static int8_t state = PAIR_NOT_START;


#define COMMUNICATE_DONE 2

#define ERROR_0 "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: keep-alive\r\nContent-Length: 11\r\n\r\n{\"error\":0}"

#define HTTP_HEAD         "HTTP/1.1 200 OK\r\n"
#define HTTP_CONTENT_TYPE "Content-Type: application/json\r\n"
#define HTTP_CONNECTION   "Connection: keep-alive\r\n"
#define HTTP_CONTENT_LEN  "Content-Length: "
#define HTTP_HEAD_OVER    "\r\n\r\n"
#define CJSON_ERROR_0     "{\"error\":0}"

static uint8_t setResponseToAPP(uint8_t *id, uint8_t *apik,uint8_t *recv_buf,uint8_t *send_buf)
{
    int8_t *pend = NULL;
    int8_t *phttp = NULL;
    int8_t *pcontent = NULL;

    cJSON *http_device_info = NULL;
    int8_t *print_device_info = NULL;
    cJSON_Hooks memoryHook;

    memoryHook.malloc_fn = pMalloc;
    memoryHook.free_fn = pFree;
    cJSON_InitHooks(&memoryHook);

    pend = (int8_t *)strstr(recv_buf, (int8_t *)"\r\n\r\n");
    if(pend != NULL)
    {
	phttp = (int8_t *)strstr(recv_buf, (int8_t *)"HTTP/1.1");
	if(phttp != NULL)
	{ 
	    pcontent = (int8_t *)strstr(recv_buf, (int8_t *) "Content-Length");
	    if(pcontent != NULL)
	    {
		debugPrint("[%s]get Dserver information\n",__FUNCTION__);
		memcpy(send_buf,(const uint8_t *)ERROR_0,strlen(ERROR_0) + 1);
		return COMMUNICATE_DONE;
	    }
	    else
	    {
		//memcpy(send_buf,(const uint8_t *)EXCHANGE_DEVICE_ID,strlen(EXCHANGE_DEVICE_ID) + 1);
		if ( (http_device_info = cJSON_CreateObject()) != NULL)
		{
		    cJSON_AddItemToObject(http_device_info ,"deviceid" ,cJSON_CreateString(id));
		    cJSON_AddItemToObject(http_device_info ,"apikey" ,cJSON_CreateString(apik));
		    cJSON_AddItemToObject(http_device_info ,"accept" ,cJSON_CreateString("post"));
		}
		else
		{
		    debugPrint("[%s] cJSON_CreateObject failed \n",__FUNCTION__);
		    return FAIL;
		}
			print_device_info = (int8_t *)cJSON_PrintUnformatted(http_device_info);
			sprintf(send_buf,"%s%s%s%s%d%s%s",HTTP_HEAD,
			HTTP_CONTENT_TYPE,
			HTTP_CONNECTION,
			HTTP_CONTENT_LEN,
			strlen(print_device_info),
			HTTP_HEAD_OVER,
			print_device_info);
			pFree(print_device_info);
			cJSON_Delete(http_device_info);
			return 1;
	    }
	}
	else
	{
	    debugPrint("[%s][warning]not a http package.\n",__FUNCTION__);
	    return -1;
	}
    }
    else
    {
	debugPrint("[%s][warning]package not finished\n",__FUNCTION__);
	return -1;
    }
}

static void parseInfo(uint8_t *recv_buf,
		       uint8_t *distri_server_name,
		       int32_t *distri_server_port,
		       uint8_t *connect_ssid,
		       uint8_t *wifi_password)
{
    cJSON *cjson_root = NULL;
    cJSON *cjson_server_name = NULL;
    cJSON *cjson_port = NULL;
    cJSON *cjson_ssid = NULL;
    cJSON *cjson_password = NULL;
    int flag = 0;

    cjson_root = cJSON_Parse(strstr(recv_buf,"\r\n\r\n"));
    if (cjson_root != NULL)
    {
	cjson_server_name = cJSON_GetObjectItem(cjson_root,"serverName");
	if (cjson_server_name != NULL)
	{
	    strcpy((char *)distri_server_name,(const char *)cjson_server_name->valuestring);
	}
	cjson_port = cJSON_GetObjectItem(cjson_root,"port");
	if (cjson_port != NULL)
	{
	    *distri_server_port = cjson_port->valueint;
	}
	cjson_password = cJSON_GetObjectItem(cjson_root,"password");
	if (cjson_password != NULL) 
	{
	    strcpy((char *)wifi_password,(const char *)cjson_password->valuestring);
	}
	cjson_ssid = cJSON_GetObjectItem(cjson_root,"ssid");
	{
	    strcpy((char*)connect_ssid,(const char *)cjson_ssid->valuestring);
	}
	cJSON_Delete(cjson_root);
    }
    
}


static int8_t communicateWitApp(uint8_t *buf)
{
    uint8_t send_buf[300] = {0};
    uint8_t deviceID[STRING_DEVICE_ID_LEN] = "wrong ID!";
    uint8_t apikey[STRING_API_KEY_LEN] = "wrong apkey!";
    uint8_t distri_server_name[STRING_DSERVER_DOMAIN_LEN] = {0}; 
    uint32_t distri_server_port = 0;
    uint8_t connect_ssid[STRING_WIFI_SSID_LEN] = {0};
    uint8_t wifi_password[STRING_WIFI_PASSWORD_LEN]= {0};
    uint8_t *tmp = NULL;
    int32_t ret;

    ret = getDeviceInfo(deviceID,apikey);
    tmp = strstr(buf,"\r\n\r\n");
    if(!tmp)
    {
	debugPrint("[%s] invaliable data\n",__FUNCTION__);
	return -1;
    }
    debugPrint("[%s]buf is \n%s\n",__FUNCTION__,tmp+2);
    ret = setResponseToAPP(deviceID,apikey,buf,send_buf);
    writeToClient(p_server,send_buf,strlen(send_buf));
    if(ret == COMMUNICATE_DONE)
    {
	parseInfo(buf,
		  distri_server_name,
		  &distri_server_port,
		  connect_ssid,
		  wifi_password);
	debugPrint("[%s]dserver_name is %s\n",__FUNCTION__,distri_server_name);
	debugPrint("[%s]port is %d\n",__FUNCTION__,distri_server_port);
	debugPrint("[%s]ssid is %s\n",__FUNCTION__,connect_ssid);
	debugPrint("[%s]password is %s\n",__FUNCTION__,wifi_password);
	ret = setDServerInfo(distri_server_name,distri_server_port);    
	ret = setConnectWifiInfo(connect_ssid,wifi_password);

	return COMMUNICATE_DONE;
    }
  
    return 0;
}

static uint8_t setupAP(void)
{
    uint8_t ap_ssid[STRING_DEVICE_ID_LEN + 6] = {'I','T','E','A','D','-',0};
    uint8_t device_id[STRING_DEVICE_ID_LEN];
    uint8_t apikey[STRING_API_KEY_LEN];
    debugPrint("[%s] set wifi ap\n",__FUNCTION__);
    getDeviceInfo(device_id,apikey);
    memcpy(ap_ssid+6,device_id,strlen(device_id));
    debugPrint("[%s] wifi ap is %s\n",__FUNCTION__,ap_ssid);
    setupWifiAP(ap_ssid);
    return 0;
}

/*step1: setup wifi ap*/
/*step2: setup socket server*/
/*step3: wait for app access*/
/*step4: get app request & return to app*/ 
/*step5: get working info & return to app*/
/*step6: if step5 is ok, return success*/

int pairWithAppRunTime(void)
{
    int32_t ret;
    uint8_t buf[500] = {0};
    switch(state)
    {
    case PAIR_NOT_START:
	debugPrint("[%s]setup wifi ap\n",__FUNCTION__);
	ret = setupAP();
	ret = PAIR_NOT_FINISHED;
	state = PAIR_AP_IS_READY;
	debugPrint("[%s] wifi AP is ready\n",__FUNCTION__);
	break;
    case PAIR_AP_IS_READY:
	debugPrint("[%s]get socket server at port 80\n",__FUNCTION__);
	p_server = setupSocketServer(80);
	if(p_server)
	{
	    state = PAIR_SERVER_OBJ_IS_READY;	    
	}
	ret = PAIR_NOT_FINISHED;
	break;
    case PAIR_SERVER_OBJ_IS_READY:
	debugPrint("[%s]wait for client\n",__FUNCTION__);
	ret = waitForClient(p_server);
	if(ret == MYSOCKETSERVER_SUCCESS)
	{
	    state = PAIR_APP_IS_ACCESS;
	}
	ret = PAIR_NOT_FINISHED;
	break;
    case PAIR_APP_IS_ACCESS:
	ret = readFromClient(p_server,buf,500);
	debugPrint("[%s] readFromClient :[%d]\n",__FUNCTION__,ret);
	debugPrint("[%s] readFromClient :[%s]\n",__FUNCTION__,buf);
	if(ret > 0)
	{
	    ret = communicateWitApp(buf);
	    if(ret == COMMUNICATE_DONE)
	    {
		debugPrint("[%s]get information done\n",__FUNCTION__);
		cleanSocketServer(&p_server);
		ret = PAIR_SUCCESS;
	    }
	    else
	    {

		closeCurrentClient(p_server);		
		state = PAIR_SERVER_OBJ_IS_READY;
		ret = PAIR_NOT_FINISHED;
	    }
	}
	else
	{
	    ret = PAIR_NOT_FINISHED;
	    closeCurrentClient(p_server);
	    state = PAIR_SERVER_OBJ_IS_READY;
	}
    }

    return ret;
}

int cleanPairWithApp(void)
{
    cleanSocketServer(&p_server);
    state = PAIR_NOT_START;
    return 0;
}

